﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using InSimDotNet;
using InSimDotNet.Packets;
using LFSRecord2.Events;
using LFSRecord2.Model;
using LFSRecord2.Model.Media;
using LFSRecord2.Model.Replay;
using LFSRecord2.Model.UserActions;
using LFSRecord2.Properties;
using LFSRecord2.Structures;
using LFSRecord2.View.Dialogs;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Win32;
using LFSRecord2.Model.Sound;

namespace LFSRecord2.ViewModel
{
    public delegate void ReplayErrorCallback(ReplayError ripError);
    public delegate void ShowGenericErrorCallback(string windowTitle, string errorMessage);

    public class MainWindowViewModel : ViewModelBase
    {
        #region Properties
        public readonly DependencyProperty WindowTitleProperty = DependencyProperty.Register(
            "WindowTitle",
            typeof(string),
            typeof(MainWindowViewModel)
        );
        public string WindowTitle
        {
            get { return (string)GetValue(WindowTitleProperty); }
            set { SetValue(WindowTitleProperty, value); }
        }

        public void UpdateWindowTitle(string projectName, string projectFilePath)
        {
            if (projectFilePath.Length > 0)
                WindowTitle = String.Format(
                        "LFS Record - {0} ({1})",
                        projectName,
                        projectFilePath);
            else
                WindowTitle = String.Format(
                        "LFS Record - {0}",
                        projectName);
        }

        public readonly DependencyProperty ConnectionInfoProperty = DependencyProperty.Register(
            "ConnectionInfo",
            typeof(string),
            typeof(MainWindowViewModel)
        );
        public string ConnectionInfo
        {
            get { return (string)GetValue(ConnectionInfoProperty); }
            set { SetValue(ConnectionInfoProperty, value); }
        }

        TimelineViewModel timelineViewModel { get; set; }
        ObservableCollection<ViewModelBase> _timelineViewModels;
        public ObservableCollection<ViewModelBase> TimelineViewModels
        {
            get
            {
                if (_timelineViewModels == null)
                    _timelineViewModels = new ObservableCollection<ViewModelBase>();
                return _timelineViewModels;
            }
        }

        ViewportViewModel viewportViewModel { get; set; }
        ObservableCollection<ViewModelBase> _viewportViewModels;
        public ObservableCollection<ViewModelBase> ViewportViewModels
        {
            get
            {
                if (_viewportViewModels == null)
                    _viewportViewModels = new ObservableCollection<ViewModelBase>();
                return _viewportViewModels;
            }
        }

        MediaViewModel mediaViewModel { get; set; }
        ObservableCollection<MediaViewModel> _mediaViewModels;
        public ObservableCollection<MediaViewModel> MediaViewModels
        {
            get
            {
                if (_mediaViewModels == null)
                    _mediaViewModels = new ObservableCollection<MediaViewModel>();
                return _mediaViewModels;
            }
        }

        private FileHistoryCollection _recentProjects;
        public FileHistoryCollection RecentProjects
        {
            get
            {
                if (_recentProjects == null)
                {
                    _recentProjects = new FileHistoryCollection(Settings.Default.RecentProjects);
                    _recentProjects.OpenFile += new EventHandler<FileHistoryEventArgs>(_recentProjects_OpenFile);
                }
                return _recentProjects;
            }
        }
        #endregion Properties

        public MainWindowViewModel()
        {
            // Controller setup
            LfsRecordController.SetupElements(new ReplayErrorCallback(ShowReplayErrorMessage), new ShowGenericErrorCallback(ShowGenericErrorMessage));
            LfsRecordController.SetupListeners();

            ConnectionInfo = "Not connected";
            UpdateWindowTitle("New Project", LfsRecordController.project.ProjectFilePath);

            // Create Timeline view model
            timelineViewModel = new TimelineViewModel();
            this.TimelineViewModels.Add(timelineViewModel);

            // Create viewport view model
            viewportViewModel = new ViewportViewModel();
            this.ViewportViewModels.Add(viewportViewModel);

            // Create media view model
            mediaViewModel = new MediaViewModel();
            this.MediaViewModels.Add(mediaViewModel);

            // Event listeners
            ServicesFactory.EventService.GetEvent<InSimConnectionChangedEvent>().Subscribe(OnInSimConnectionChanged, ThreadOption.UIThread);
            ServicesFactory.EventService.GetEvent<InSimErrorRaisedEventArgs>().Subscribe(OnInSimErrorRaised, ThreadOption.UIThread);

            //// Check newer version
            //Uri versionFileUrl = new Uri("http://www.lfs.net/LFSRecord/version.txt");
            //WebClient wc = new WebClient();
            //try
            //{
            //    string versionFile = wc.DownloadString(versionFileUrl).Trim();
            //    for (int i = 0; i < wc.ResponseHeaders.Count; i++)
            //        Debug.WriteLine("\t" + wc.Headers.GetKey(i) + " = " + wc.ResponseHeaders.Get(i));

            //    Debug.WriteLine("Latest version : " + versionFile);
            //}
            //catch (WebException ex)
            //{
            //    Debug.WriteLine("Download of version.txt failed : " + ex.Message);
            //}
        }

        public void WindowClosing()
        {
            Debug.WriteLine("Window closing... saving settings");

            // Save settings.
            Settings.Default.RecentProjects = RecentProjects.ToStringCollection();
            Settings.Default.Save();
        }

        #region Dialogs
        private void ShowLoadProjectDialog()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.DefaultExt = ".lfsrec";
            dlg.Filter = "LFS Record Projects (*.lfsrec)|*.lfsrec";
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;

            if (dlg.ShowDialog() ?? false)
            {
                LoadProject(dlg.FileName);
                UpdateWindowTitle(LfsRecordController.project.ProjectName, LfsRecordController.project.ProjectFilePath);
            }
        }

        public bool ShowSaveAsDialogBox()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            if (LfsRecordController.project.ProjectFilePath.Length > 0)
                dlg.FileName = Path.GetFileNameWithoutExtension(LfsRecordController.project.ProjectFilePath);
            else
                dlg.FileName = LfsRecordController.project.ProjectName;
            dlg.DefaultExt = ".lfsrec";
            dlg.Filter = "LFS Record Projects (*.lfsrec)|*.lfsrec";
            dlg.AddExtension = true;
            dlg.OverwritePrompt = true;

            if (dlg.ShowDialog() ?? false)
            {
                try
                {
                    LfsRecordController.SaveProject(dlg.FileName);

                    RecentProjects.Add(new FileHistoryInfo(dlg.FileName));

                    UpdateWindowTitle(LfsRecordController.project.ProjectName, LfsRecordController.project.ProjectFilePath);

                    return true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        App.Current.MainWindow,
                        ex.Message,
                        "File Error",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }
            }

            return false;
        }

        /// <summary>
        /// Dialog for importing media files. Auto-detects filetype.
        /// </summary>
        public static void ShowImportMediaDialog()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Import media";
            dlg.Filter = "All Known Media Formats|*.lfsrd;*.wav;*.aif|LFS Replay Data|*.lfsrd|Audio Files|*.wav;*.aif|All Files|*.*";
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;

            if (dlg.ShowDialog() ?? false)
            {
                try
                {
                    if (dlg.FileName.Length > 255)
                    {
                        throw (new MediaImportException("File path is too long. Allowed length is 255 characters."));
                    }

                    MediaBase mb = MediaBase.MediaFactory(dlg.FileName);
                    if (mb == null)
                        throw new MediaImportException("Unknown media file type.");

                    LfsRecordController.project.DeselectAllMedia();
                    mb.IsSelected = true;
                    int mediaIndex = LfsRecordController.project.HasMediaFile(dlg.FileName);
                    if (mediaIndex > -1)
                    {
                        // Overwrite existing MediaObject
                        LfsRecordController.project.MediaObjects[mediaIndex] = mb;
                    }
                    else
                    {
                        LfsRecordController.project.MediaObjects.Add(mb);
                    }

                    if (mb.MediaType == MediaTypes.Audio)
                        LfsRecordController.project.ReattachBrokenStreams((MediaAudio)mb);

                    LfsRecordController.project.Edited = true;
                    Debug.WriteLine("Loaded media file - " + mb.FilePath + " - of type : {0}", mb.MediaType);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        App.Current.MainWindow,
                        ex.Message,
                        "Import Error",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }
            }
        }

        /// <summary>
        /// Dialog for importing replay data (all data, not just MCI) and saving that data to a file afterwards, which can then be imported into the mediaview.
        /// </summary>
        public static void ShowImportReplayDataFromLFSDialog()
        {
            // We remove the default insim listeners because the replay recording process takes control of the insim connection
            LfsRecordController.RemoveDefaultInSimListeners(false);

            ImportRpdFromLfs importRpd = LfsRecordController.CreateImportRpdFromLfs();
            ImportRpdFromLfsDialog dlg = new ImportRpdFromLfsDialog();
            dlg.DataContext = importRpd;
            dlg.Owner = App.Current.MainWindow;

            if (dlg.ShowDialog() ?? false)
            {
                try
                {
                    // Save the replay data from importRpd
                    SaveFileDialog sdlg = new SaveFileDialog();
                    sdlg.Title = "Save replay data";
                    sdlg.FileName = Path.GetFileNameWithoutExtension(importRpd.RData.ReplayName);
                    sdlg.DefaultExt = ".lfsrd";
                    sdlg.Filter = "LFS Replay Data (*.lfsrd)|*.lfsrd";
                    sdlg.AddExtension = true;
                    sdlg.OverwritePrompt = true;

                    if (sdlg.ShowDialog() ?? false)
                    {
                        if (sdlg.FileName.Length > 255)
                        {
                            throw (new MediaImportException("File path is too long. Allowed length is 255 characters."));
                        }
                        importRpd.RData.Save(sdlg.FileName);

                        // Import the newly created lfsrd file into our project, as a Media object
                        MediaReplayData mrpd = new MediaReplayData(sdlg.FileName);
                        LfsRecordController.project.DeselectAllMedia();
                        mrpd.IsSelected = true;
                        int mediaIndex = LfsRecordController.project.HasMediaFile(sdlg.FileName);
                        if (mediaIndex > -1)
                        {
                            // Overwrite existing MediaObject
                            LfsRecordController.project.MediaObjects[mediaIndex] = mrpd;
                        }
                        else
                        {
                            LfsRecordController.project.MediaObjects.Add(mrpd);
                        }
                        LfsRecordController.project.Edited = true;
                        Debug.WriteLine("Loaded replay data - " + mrpd.RData.ReplayName);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        App.Current.MainWindow,
                        ex.Message,
                        "Import Error",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }
            }

            importRpd.Dispose();
            importRpd = null;
            dlg.DataContext = null;
            LfsRecordController.AddDefaultInSimListeners(true);
        }

        public static bool ShowExportCustomPathDialogBox()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.FileName = LfsRecordController.project.ProjectName;
            dlg.DefaultExt = ".lfspath";
            dlg.Filter = "LFS Record Custom Path (*.lfspath)|*.lfspath";

            if (dlg.ShowDialog() ?? false)
            {
                return LfsRecordController.ExportLFSRecordCustomPath(dlg.FileName);
            }

            return false;
        }

        public static bool ShowExportAEPathDialogBox()
        {
            // Export selection dialog (ask what to export)
            ExportAEDialogDataTemplate exportTemplate = new ExportAEDialogDataTemplate();
            exportTemplate.PopulateTemplate(LfsRecordController.project);
            ExportAEDialogBox exportDlg = new ExportAEDialogBox();
            exportDlg.DataContext = exportTemplate;
            exportDlg.Owner = Application.Current.MainWindow;

            exportDlg.ShowDialog();

            if (exportDlg.DialogResult != true)
                return false;

            if (exportTemplate.SelectedLayers.Count == 0)
            {
                MessageBox.Show(Application.Current.MainWindow, "No layers were selected for export.", "Export Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            else if (exportTemplate.EndFrame - exportTemplate.StartFrame < 0)
            {
                MessageBox.Show(Application.Current.MainWindow, "Cannot export negative frames (check Start and End frame in export dialog).", "Export Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            else if (exportTemplate.MovieWidth <= 0 || exportTemplate.MovieHeight <= 0)
            {
                MessageBox.Show(Application.Current.MainWindow, "Invalid movie dimensions (equal or less than 0).", "Export Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }

            // Save file dialog
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.FileName = LfsRecordController.project.ProjectName; // Default file name
            dlg.DefaultExt = ".jsx"; // Default file extension
            dlg.Filter = "AFX JavaScript File (*.jsx)|*.jsx"; // Filter files by extension

            Nullable<bool> result = dlg.ShowDialog();

            bool ok = false;
            if (result == true)
            {
                ok = LfsRecordController.ExportAFXPath(exportTemplate, dlg.FileName);
            }

            return ok;
        }

        public void ShowLoadTrackDialogBox()
        {
            LoadTrackDialogBox dlg = new LoadTrackDialogBox();
            LoadTrackDialogDataTemplate dt = new LoadTrackDialogDataTemplate();
            dt.SelectedTrack = "Blackwood";
            dlg.DataContext = dt;
            dlg.Owner = Application.Current.MainWindow;

            dlg.ShowDialog();

            if (dlg.DialogResult == true)
            {
                viewportViewModel.CurrentTrack = dt.SelectedTrack;
            }
        }

        public void ShowProjectSettingsDialogBox()
        {
            ProjectConfigurationDialogBox dlg = new ProjectConfigurationDialogBox();
            LfsRecordProjectSettings projectSettings = LfsRecordController.project.GetSettingsObject();
            dlg.DataContext = projectSettings;
            dlg.Owner = Application.Current.MainWindow;

            UserActionProjectSettingsChanged settingsAction = new UserActionProjectSettingsChanged()
                {
                    OldProjectName = projectSettings.ProjectName,
                    OldProjectFilePath = projectSettings.ProjectFilePath,
                    OldProjectLength = projectSettings.ProjectLength,
                    OldFPS = projectSettings.FPS,
                    OldInSimIP = projectSettings.InSimIP,
                    OldInSimPort = projectSettings.InSimPort,
                    OldInSimAdminPassword = projectSettings.InSimAdminPassword,
                    OldRecordFullScreenOverride = projectSettings.RecordFullScreenOverride,
                    OldRecordStopHotkey = projectSettings.RecordStopHotkey,
                    OldRecordingWidth = projectSettings.RecordingWidth,
                    OldRecordingHeight = projectSettings.RecordingHeight,
                    OldRecordHide2D = projectSettings.RecordHide2D,
                    OldMove3DAxesInvert = projectSettings.Move3DAxesInvert,
                    OldAudioOutputDevice = projectSettings.AudioOutputDevice,
                    OldSampleRate = projectSettings.SampleRate
                };

            dlg.ShowDialog();

            if (dlg.DialogResult == true)
            {
                // New project name? If so, we should rename a saved project file.
                if (LfsRecordController.project.ProjectName != projectSettings.ProjectName)
                {
                    LfsRecordController.project.ProjectName = projectSettings.ProjectName;
                    UpdateWindowTitle(LfsRecordController.project.ProjectName, LfsRecordController.project.ProjectFilePath);
                }

                if (LfsRecordController.project.ProjectLength != projectSettings.ProjectLength)
                {
                    LfsRecordController.project.ProjectLength = projectSettings.ProjectLength;
                    timelineViewModel.ProjectLength = projectSettings.ProjectLength;
                }

                if (LfsRecordController.project.FPS != projectSettings.FPS)
                {
                    LfsRecordController.project.FPSChangeStretch(LfsRecordController.project.FPS, projectSettings.FPS);
                    LfsRecordController.project.FPS = projectSettings.FPS;
                }

                if (LfsRecordController.Mixer.CurrentWaveOutDevice != projectSettings.AudioOutputDevice ||
                    LfsRecordController.Mixer.SampleRate != projectSettings.SampleRate)
                {
                    if (LfsRecordController.PlaybackState != PlaybackStates.Stopped)
                        LfsRecordController.StopPlayback();
                    LfsRecordController.Mixer.ResetIO(projectSettings.AudioOutputDevice, 2, 16, projectSettings.SampleRate);
                }

                LfsRecordController.project.InSimIP = projectSettings.InSimIP;
                LfsRecordController.project.InSimPort = projectSettings.InSimPort;
                LfsRecordController.project.InSimAdminPassword = projectSettings.InSimAdminPassword;

                LfsRecordController.project.RecordFullScreenOverride = projectSettings.RecordFullScreenOverride;
                LfsRecordController.project.RecordStopHotkey = projectSettings.RecordStopHotkey;
                LfsRecordController.project.RecordingWidth = projectSettings.RecordingWidth;
                LfsRecordController.project.RecordingHeight = projectSettings.RecordingHeight;
                LfsRecordController.project.RecordHide2D = projectSettings.RecordHide2D;

                LfsRecordController.project.Move3DAxesInvert = projectSettings.Move3DAxesInvert;

                LfsRecordController.project.Edited = true;

                settingsAction.NewProjectName = LfsRecordController.project.ProjectName;
                settingsAction.NewProjectFilePath = LfsRecordController.project.ProjectFilePath;
                settingsAction.NewProjectLength = LfsRecordController.project.ProjectLength;
                settingsAction.NewFPS = LfsRecordController.project.FPS;
                settingsAction.NewInSimIP = LfsRecordController.project.InSimIP;
                settingsAction.NewInSimPort = LfsRecordController.project.InSimPort;
                settingsAction.NewInSimAdminPassword = LfsRecordController.project.InSimAdminPassword;
                settingsAction.NewRecordFullScreenOverride = LfsRecordController.project.RecordFullScreenOverride;
                settingsAction.NewRecordStopHotkey = LfsRecordController.project.RecordStopHotkey;
                settingsAction.NewRecordingWidth = LfsRecordController.project.RecordingWidth;
                settingsAction.NewRecordingHeight = LfsRecordController.project.RecordingHeight;
                settingsAction.NewRecordHide2D = LfsRecordController.project.RecordHide2D;
                settingsAction.NewMove3DAxesInvert = LfsRecordController.project.Move3DAxesInvert;
                settingsAction.NewAudioOutputDevice = LfsRecordController.Mixer.CurrentWaveOutDevice;
                settingsAction.NewSampleRate = LfsRecordController.Mixer.SampleRate;

                if (settingsAction.NewProjectName != settingsAction.OldProjectName ||
                    settingsAction.NewProjectFilePath != settingsAction.OldProjectFilePath ||
                    settingsAction.NewProjectLength != settingsAction.OldProjectLength ||
                    settingsAction.NewFPS != settingsAction.OldFPS ||
                    settingsAction.NewInSimIP != settingsAction.OldInSimIP ||
                    settingsAction.NewInSimPort != settingsAction.OldInSimPort ||
                    settingsAction.NewInSimAdminPassword != settingsAction.OldInSimAdminPassword ||
                    settingsAction.NewRecordFullScreenOverride != settingsAction.OldRecordFullScreenOverride ||
                    settingsAction.NewRecordStopHotkey != settingsAction.OldRecordStopHotkey ||
                    settingsAction.NewRecordingWidth != settingsAction.OldRecordingWidth ||
                    settingsAction.NewRecordingHeight != settingsAction.OldRecordingHeight ||
                    settingsAction.NewRecordHide2D != settingsAction.OldRecordHide2D ||
                    settingsAction.NewMove3DAxesInvert != settingsAction.OldMove3DAxesInvert ||
                    settingsAction.NewAudioOutputDevice != settingsAction.OldAudioOutputDevice ||
                    settingsAction.NewSampleRate != settingsAction.OldSampleRate)
                {
                    LfsRecordController.RecordUserAction(settingsAction);
                }
                LfsRecordController.CreateUndoLevel();
            }
        }

        public static void ShowAboutDialogBox()
        {
            AboutDialogBox dlg = new AboutDialogBox();
            dlg.Owner = Application.Current.MainWindow;
            dlg.ShowDialog();
        }
        #endregion Dialogs

        #region MenuHandlers
        #region FileMenu
        public bool CanDestroyProject()
        {
            if (LfsRecordController.project.Edited)
            {
                string message = String.Format(
                    "Do you want to save the project '{0}' before continuing?",
                    LfsRecordController.project.ProjectName);

                MessageBoxResult mbResult = MessageBox.Show(
                    Application.Current.MainWindow,
                    message,
                    "Save Current Project",
                    MessageBoxButton.YesNoCancel,
                    MessageBoxImage.Question);

                if (mbResult == MessageBoxResult.Cancel)
                {
                    return false;
                }
                else if (mbResult == MessageBoxResult.Yes)
                {
                    return SaveCommandExecute();
                }
            }

            return true;
        }

        RelayCommand _newCommand;
        public ICommand NewCommand
        {
            get
            {
                if (_newCommand == null)
                    _newCommand = new RelayCommand(param => this.NewCommandExecute());
                return _newCommand;
            }
        }

        public void NewCommandExecute()
        {
            if (CanDestroyProject())
            {
                // Set blank project
                LfsRecordController.DisposeProject();

                // Set project to default (with default layer(s))
                LfsRecordController.DefaultComposition("New Project", true);
                timelineViewModel.ProjectLength = LfsRecordController.project.ProjectLength;
                timelineViewModel.ProjectFPS = LfsRecordController.project.FPS;

                ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(new FrameIndexChangedEventArgs() { Sender = this, NewFrameIndex = 0, UpdateCurrentValues = true });

                UpdateWindowTitle(LfsRecordController.project.ProjectName, LfsRecordController.project.ProjectFilePath);
            }
        }

        RelayCommand _loadCommand;
        public ICommand LoadCommand
        {
            get
            {
                if (_loadCommand == null)
                    _loadCommand = new RelayCommand(param => this.LoadCommandExecute());
                return _loadCommand;
            }
        }

        public void LoadCommandExecute()
        {
            if (CanDestroyProject())
            {
                ShowLoadProjectDialog();
            }
        }

        private void LoadProject(string projectFileName)
        {
            try
            {
                Mouse.OverrideCursor = Cursors.Wait;

                LfsRecordController.LoadProject(projectFileName);

                RecentProjects.Add(new FileHistoryInfo(projectFileName));

                UpdateWindowTitle(LfsRecordController.project.ProjectName, LfsRecordController.project.ProjectFilePath);
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    App.Current.MainWindow,
                    ex.Message,
                    "File Error",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }

        public void LoadProjectFromArgs(string[] args)
        {
            if (args != null && args.Any())
            {
                string projectFileName = args.First();

                if (File.Exists(projectFileName))
                {
                    LoadProject(projectFileName);
                }
                else
                {
                    string message = String.Format(
                        "The project file '{0}' cannot be loaded as it does not exist",
                        projectFileName);

                    MessageBox.Show(
                        App.Current.MainWindow,
                        message,
                        "Invalid Command Line Arguments",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }
            }
        }

        private void _recentProjects_OpenFile(object sender, FileHistoryEventArgs e)
        {
            if (File.Exists(e.File.FileName))
            {
                if (CanDestroyProject())
                {
                    LoadProject(e.File.FileName);

                    e.IsOpened = true;
                }
            }
            else
            {
                string message = String.Format(
                    "The project file '{0}' no longer exists, do you want to remove it from the recent projects list?",
                    e.File.FileName);

                MessageBoxResult mbResult = MessageBox.Show(
                    App.Current.MainWindow,
                    message,
                    "Recent Project Missing",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Question);

                if (mbResult == MessageBoxResult.Yes)
                {
                    RecentProjects.Remove(e.File);
                }
            }
        }

        // SaveCommand
        RelayCommand _saveCommand;
        public ICommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                    _saveCommand = new RelayCommand(param => this.SaveCommandExecute(), param => SaveCommandCanExecute());
                return _saveCommand;
            }
        }
        public bool SaveCommandExecute()
        {
            if (!LfsRecordController.project.Edited)
                return true;

            if (String.IsNullOrEmpty(LfsRecordController.project.ProjectFilePath))
                return ShowSaveAsDialogBox();

            try
            {
                LfsRecordController.SaveProject(LfsRecordController.project.ProjectFilePath);

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    App.Current.MainWindow,
                    ex.Message,
                    "File Error",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
            return false;
        }

        static bool SaveCommandCanExecute()
        {
            return (LfsRecordController.project.Edited) ? true : false;
        }

        // SaveAsCommand
        RelayCommand _saveAsCommand;
        public ICommand SaveAsCommand
        {
            get
            {
                if (_saveAsCommand == null)
                    _saveAsCommand = new RelayCommand(param => this.SaveAsCommandExecute());
                return _saveAsCommand;
            }
        }
        void SaveAsCommandExecute()
        {
            ShowSaveAsDialogBox();
        }

        // ImportMediaCommand
        RelayCommand _importMediaCommand;
        public ICommand ImportMediaCommand
        {
            get
            {
                if (_importMediaCommand == null)
                    _importMediaCommand = new RelayCommand(param => ImportMediaCommandExecute());
                return _importMediaCommand;
            }
        }
        static void ImportMediaCommandExecute()
        {
            ShowImportMediaDialog();
        }

        // ImportReplayDataFromLFSCommand
        RelayCommand _importReplayDataFromLFSCommand;
        public ICommand ImportReplayDataFromLFSCommand
        {
            get
            {
                if (_importReplayDataFromLFSCommand == null)
                    _importReplayDataFromLFSCommand = new RelayCommand(
                        param => ImportReplayDataFromLFSCommandExecute(),
                        param => ImportReplayDataFromLFSCommandCanExecute());
                return _importReplayDataFromLFSCommand;
            }
        }
        static void ImportReplayDataFromLFSCommandExecute()
        {
            ShowImportReplayDataFromLFSDialog();
        }
        static bool ImportReplayDataFromLFSCommandCanExecute()
        {
            return (LfsRecordController.project.HasReplay() && LfsRecordController.insim != null && LfsRecordController.insim.IsConnected);
        }

        // ExportCustomPathCommand
        RelayCommand _exportCustomPathCommand;
        public ICommand ExportCustomPathCommand
        {
            get
            {
                if (_exportCustomPathCommand == null)
                    _exportCustomPathCommand = new RelayCommand(param => ExportCustomPathCommandExecute(),
                                                                param => ExportCustomPathCommandCanExecute());
                return _exportCustomPathCommand;
            }
        }
        static void ExportCustomPathCommandExecute()
        {
            ShowExportCustomPathDialogBox();
        }
        static bool ExportCustomPathCommandCanExecute()
        {
            int activeCamIndex = LfsRecordController.project.GetActiveCameraLayerIndex();
            if (activeCamIndex < 0)
                return false;

            if (LfsRecordController.project.Layers[activeCamIndex].LastKeyFrameNum() < 0)
                return false;

            return true;
        }

        // ExportAFXPathCommand
        RelayCommand _exportAEPathCommand;
        public ICommand ExportAEPathCommand
        {
            get
            {
                if (_exportAEPathCommand == null)
                    _exportAEPathCommand = new RelayCommand(param => ExportAEPathCommandExecute(), param => ExportAEPathCommandCanExecute());
                return _exportAEPathCommand;
            }
        }
        static void ExportAEPathCommandExecute()
        {
            ShowExportAEPathDialogBox();
        }
        static bool ExportAEPathCommandCanExecute()
        {
            return (LfsRecordController.project.FPS < 100);
        }

        //ExitCommand
        RelayCommand _exitCommand;
        public ICommand ExitCommand
        {
            get
            {
                if (_exitCommand == null)
                    _exitCommand = new RelayCommand(param => ExitCommandExecute());
                return _exitCommand;
            }
        }
        static void ExitCommandExecute()
        {
            Application.Current.MainWindow.Close();
        }

        #endregion FileMenu

        #region EditMenu

        // UndoCommand
        RelayCommand _undoCommand;
        public ICommand UndoCommand
        {
            get
            {
                if (_undoCommand == null)
                    _undoCommand = new RelayCommand(param => UndoCommandExecute(), param => UndoCommandCanExecute());
                return _undoCommand;
            }
        }
        static void UndoCommandExecute()
        {
            LfsRecordController.ApplyUndo();
        }
        static bool UndoCommandCanExecute()
        {
            return (LfsRecordController.CanUndo()) ? true : false;
        }

        // RedoCommand
        RelayCommand _redoCommand;
        public ICommand RedoCommand
        {
            get
            {
                if (_redoCommand == null)
                    _redoCommand = new RelayCommand(param => RedoCommandExecute(), param => RedoCommandCanExecute());
                return _redoCommand;
            }
        }
        static void RedoCommandExecute()
        {
            LfsRecordController.ApplyRedo();
        }
        static bool RedoCommandCanExecute()
        {
            return (LfsRecordController.CanRedo()) ? true : false;
        }

        // CutCommand
        RelayCommand _cutCommand;
        public ICommand CutCommand
        {
            get
            {
                if (_cutCommand == null)
                    _cutCommand = new RelayCommand(param => CutCommandExecute());
                return _cutCommand;
            }
        }
        static void CutCommandExecute()
        {
            LfsRecordController.project.CutSelectedKeyFrames();
            LfsRecordController.CreateUndoLevel();
        }

        // CopyCommand
        RelayCommand _copyCommand;
        public ICommand CopyCommand
        {
            get
            {
                if (_copyCommand == null)
                    _copyCommand = new RelayCommand(param => CopyCommandExecute());
                return _copyCommand;
            }
        }
        static void CopyCommandExecute()
        {
            LfsRecordController.project.CopySelectedKeyFrames();
            LfsRecordController.CreateUndoLevel();
        }

        // PasteCommand
        RelayCommand _pasteCommand;
        public ICommand PasteCommand
        {
            get
            {
                if (_pasteCommand == null)
                    _pasteCommand = new RelayCommand(param => PasteCommandExecute());
                return _pasteCommand;
            }
        }
        static void PasteCommandExecute()
        {
            LfsRecordController.project.PasteSelectedKeyFrames();
            LfsRecordController.CreateUndoLevel();
        }

        #endregion EditMenu

        #region ViewMenu

        //LoadTrackCommand
        RelayCommand _loadTrackCommand;
        public ICommand LoadTrackCommand
        {
            get
            {
                if (_loadTrackCommand == null)
                    _loadTrackCommand = new RelayCommand(param => LoadTrackCommandExecute());
                return _loadTrackCommand;
            }
        }

        public void LoadTrackCommandExecute()
        {
            ShowLoadTrackDialogBox();
        }

        #endregion ViewMenu

        #region LayersMenu

        //AddCameraLayerCommand
        RelayCommand _addCameraLayerCommand;
        public ICommand AddCameraLayerCommand
        {
            get
            {
                if (_addCameraLayerCommand == null)
                    _addCameraLayerCommand = new RelayCommand(param => AddCameraLayerCommandExecute(param));
                return _addCameraLayerCommand;
            }
        }
        static public void AddCameraLayerCommandExecute(object param)
        {
            CompLayerBase l = null;
            if ((string)param == "Look")
                l = LfsRecordController.project.AddLookCamera();
            else
                l = LfsRecordController.project.AddFreeCamera();
            if (l != null)
            {
                LfsRecordController.RecordUserAction(new UserActionAddLayer()
                {
                    LayerType = l.LayerType,
                    LayerIndex = l.LayerIndex,
                    LayerName = l.LayerName
                });
            }
            LfsRecordController.CreateUndoLevel();
        }

        //AddNullLayerCommand
        RelayCommand _addNullLayerCommand;
        public ICommand AddNullLayerCommand
        {
            get
            {
                if (_addNullLayerCommand == null)
                    _addNullLayerCommand = new RelayCommand(param => AddNullLayerCommandExecute());
                return _addNullLayerCommand;
            }
        }
        public static void AddNullLayerCommandExecute()
        {
            CompLayerBase l = null;
            l = LfsRecordController.project.AddNull();
            if (l != null)
            {
                LfsRecordController.RecordUserAction(new UserActionAddLayer()
                {
                    LayerType = l.LayerType,
                    LayerIndex = l.LayerIndex,
                    LayerName = l.LayerName
                });
            }
            LfsRecordController.CreateUndoLevel();
        }

        // AddReplayControlLayerCommand
        RelayCommand _addReplayControlLayerCommand;
        public ICommand AddReplayControlLayerCommand
        {
            get
            {
                if (_addReplayControlLayerCommand == null)
                    _addReplayControlLayerCommand = new RelayCommand(param => AddReplayControlLayerCommandExecute(), param => AddReplayControlLayerCommandCanExecute());
                return _addReplayControlLayerCommand;
            }
        }
        public static void AddReplayControlLayerCommandExecute()
        {
            CompLayerBase l = null;
            l = LfsRecordController.project.AddReplayControl();
            if (l != null)
            {
                LfsRecordController.RecordUserAction(new UserActionAddLayer()
                {
                    LayerType = l.LayerType,
                    LayerIndex = l.LayerIndex,
                    LayerName = l.LayerName
                });
            }
            LfsRecordController.CreateUndoLevel();
        }
        static bool AddReplayControlLayerCommandCanExecute()
        {
            return (LfsRecordController.project.NumLayersOfType(CompLayerTypes.ReplayControl) > 0) ? false : true;
        }

        // AddReplayDataLayerCommand
        RelayCommand _addReplayDataLayerCommand;
        public ICommand AddReplayDataLayerCommand
        {
            get
            {
                if (_addReplayDataLayerCommand == null)
                    _addReplayDataLayerCommand = new RelayCommand(param => AddReplayDataLayerCommandExecute());
                return _addReplayDataLayerCommand;
            }
        }
        public static void AddReplayDataLayerCommandExecute()
        {
            CompLayerBase l = null;
            l = LfsRecordController.project.AddReplayData();
            if (l != null)
            {
                LfsRecordController.RecordUserAction(new UserActionAddLayer()
                {
                    LayerType = l.LayerType,
                    LayerIndex = l.LayerIndex,
                    LayerName = l.LayerName
                });
            }
            LfsRecordController.CreateUndoLevel();
        }

        // AddAudioLayerCommand
        RelayCommand _addAudioLayerCommand;
        public ICommand AddAudioLayerCommand
        {
            get
            {
                if (_addAudioLayerCommand == null)
                    _addAudioLayerCommand = new RelayCommand(param => AddAudioLayerCommandExecute());
                return _addAudioLayerCommand;
            }
        }
        public static void AddAudioLayerCommandExecute()
        {
            AudioLayer l = null;
            l = LfsRecordController.project.AddAudio();
            if (l != null)
            {
                LfsRecordController.RecordUserAction(new UserActionAddLayer()
                {
                    LayerType = l.LayerType,
                    LayerIndex = l.LayerIndex,
                    LayerName = l.LayerName
                });
            }
            LfsRecordController.CreateUndoLevel();
        }

        // DeleteSelectedLayersCommand
        RelayCommand _deleteSelectedLayersCommand;
        public ICommand DeleteSelectedLayersCommand
        {
            get
            {
                if (_deleteSelectedLayersCommand == null)
                    _deleteSelectedLayersCommand = new RelayCommand(param => DeleteSelectedLayersCommandExecute(), param => DeleteSelectedLayersCommandCanExecute());
                return _deleteSelectedLayersCommand;
            }
        }
        static void DeleteSelectedLayersCommandExecute()
        {
            LfsRecordController.project.DeleteSelectedLayers();
            LfsRecordController.CreateUndoLevel();
        }
        static bool DeleteSelectedLayersCommandCanExecute()
        {
            return (LfsRecordController.project.HasSelectedLayers()) ? true : false;
        }

        #endregion LayersMenu

        #region ProjectMenu

        //ProjectConfigCommand
        RelayCommand _projectSettingsCommand;
        public ICommand ProjectSettingsCommand
        {
            get
            {
                if (_projectSettingsCommand == null)
                    _projectSettingsCommand = new RelayCommand(param => ProjectSettingsCommandExecute());
                return _projectSettingsCommand;
            }
        }
        void ProjectSettingsCommandExecute()
        {
            ShowProjectSettingsDialogBox();
        }

        //InSimConnectCommand
        RelayCommand _inSimConnectCommand;
        public ICommand InSimConnectCommand
        {
            get
            {
                if (_inSimConnectCommand == null)
                    _inSimConnectCommand = new RelayCommand(param => InSimConnectCommandExecute(param), param => InSimConnectCommandCanExecute(param));
                return _inSimConnectCommand;
            }
        }
        static public void InSimConnectCommandExecute(object connect)
        {
            if ((string)connect == "1")
            {
                Mouse.OverrideCursor = Cursors.Wait;
                try
                {
                    if (LfsRecordController.insim == null || !LfsRecordController.insim.IsConnected)
                        LfsRecordController.ConnectInSim();
                    Mouse.OverrideCursor = null;
                }
                catch (SocketException ex)
                {
                    Mouse.OverrideCursor = null;
                    MessageBox.Show(Application.Current.MainWindow, ex.Message, "Insim error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                catch (InSimException e)
                {
                    Mouse.OverrideCursor = null;
                    MessageBox.Show(Application.Current.MainWindow, e.Message, "Insim error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                if (LfsRecordController.insim.IsConnected)
                    LfsRecordController.DisconnectInSim();
            }
        }
        static bool InSimConnectCommandCanExecute(object connect)
        {
            if ((string)connect == "1")
            {
                return (LfsRecordController.insim == null || !LfsRecordController.insim.IsConnected) ? true : false;
            }
            else
            {
                return (LfsRecordController.insim != null && LfsRecordController.insim.IsConnected) ? true : false;
            }
        }

        #endregion ProjectMenu

        #region TimelineMenu

        // PaceSelectedKeysCommand
        RelayCommand _paceSelectedKeysCommand;
        public ICommand PaceSelectedKeysCommand
        {
            get
            {
                if (_paceSelectedKeysCommand == null)
                    _paceSelectedKeysCommand = new RelayCommand(param => PaceSelectedKeysCommandExecute(), param => PaceSelectedKeysCommandCanExecute());
                return _paceSelectedKeysCommand;
            }
        }
        static void PaceSelectedKeysCommandExecute()
        {
            if (!LfsRecordController.project.PaceSelectedKeyFrames())
            {
                MessageBox.Show(Application.Current.MainWindow, "Cannot pace keyframes. Make sure the keyframe selection is contiguous.", "Pacing error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            else
            {
                LfsRecordController.CreateUndoLevel();
            }
        }
        static bool PaceSelectedKeysCommandCanExecute()
        {
            // See if all selected keys are on one timeline and are contiguous
            return LfsRecordController.project.VerifySelectionIsContiguous();
        }

        //StartStopCommand
        RelayCommand _startStopCommand;
        public ICommand StartStopCommand
        {
            get
            {
                if (_startStopCommand == null)
                    _startStopCommand = new RelayCommand(param => StartStopCommandExecute());
                return _startStopCommand;
            }
        }
        static public void StartStopCommandExecute()
        {
            if (LfsRecordController.PlaybackState == PlaybackStates.Stopped)
                LfsRecordController.StartPlayback();
            else
                LfsRecordController.StopPlayback();
        }

        //StopToStartCommand
        RelayCommand _stopToStartCommand;
        public ICommand StopToStartCommand
        {
            get
            {
                if (_stopToStartCommand == null)
                    _stopToStartCommand = new RelayCommand(param => StopToStartCommandExecute());
                return _stopToStartCommand;
            }
        }
        static void StopToStartCommandExecute()
        {
            LfsRecordController.StopPlayback();
        }

        //StartRecordCommand
        RelayCommand _startRecordCommand;
        public ICommand StartRecordCommand
        {
            get
            {
                if (_startRecordCommand == null)
                    _startRecordCommand = new RelayCommand(param => StartRecordCommandExecute(), param => StartRecordcommandCanExecute());
                return _startRecordCommand;
            }
        }
        static void StartRecordCommandExecute()
        {
            LfsRecordController.StartRecord();
        }
        static bool StartRecordcommandCanExecute()
        {
            return (LfsRecordController.insim == null || !LfsRecordController.insim.IsConnected) ? false : true;
        }

        #endregion TimelineMenu

        #region HelpMenu

        //AboutCommand
        RelayCommand _aboutCommand;
        public ICommand AboutCommand
        {
            get
            {
                if (_aboutCommand == null)
                    _aboutCommand = new RelayCommand(param => AboutCommandExecute());
                return _aboutCommand;
            }
        }
        public static void AboutCommandExecute()
        {
            ShowAboutDialogBox();
        }

        #endregion HelpMenu

        #endregion MenuHandlers

        public void OnInSimConnectionChanged(bool connected)
        {
            ConnectionInfo = connected ? "Connected" : "Not connected";
        }

        public void OnInSimErrorRaised(Exception exception)
        {
            ShowGenericErrorMessage("InSim Error", String.Format(
                "An exception occured on the internal receive thread: {0}",
                exception.Message));
        }

        public void ShowGenericErrorMessage(string caption, string errorMessage)
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
                {
                    MessageBox.Show(
                        Application.Current.MainWindow,
                        errorMessage,
                        caption,
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }));
        }

        public void ShowReplayErrorMessage(ReplayError ripError)
        {
            string errStr = GetReplayErrorString(ripError);

            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
                {
                    MessageBox.Show(
                        Application.Current.MainWindow,
                        "Replay error : " + errStr,
                        "Replay Error",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }));
        }

        private static string GetReplayErrorString(ReplayError ripError)
        {
            switch (ripError)
            {
                case ReplayError.RIP_CORRUPTED:
                    return "The replay file appears corrupted.";
                case ReplayError.RIP_DEDICATED:
                    return "You are connected to a dedicated host, which cannot play replays.";
                case ReplayError.RIP_DEST_OOB:
                    return "The position you want to play from is beyond the length of the replay.";
                case ReplayError.RIP_NOT_FOUND:
                    return "The replay does not seem to exist. Have you made a typo in the filename? Also be sure to include the .spr or .mpr extension.";
                case ReplayError.RIP_NOT_REPLAY:
                    return "LFS Record did not provide a replay name for LFS to load.";
                case ReplayError.RIP_OOS:
                    return "The replay went Out Of Sync and cannot be used.";
                case ReplayError.RIP_UNKNOWN:
                    return "There was an unknown replay error.";
                case ReplayError.RIP_UNLOADABLE:
                    return "The replay does seem to exist, but LFS cannot load it.";
                case ReplayError.RIP_USER:
                    return "You interrupted the replay action from inside LFS.";
                case ReplayError.RIP_WRONG_MODE:
                    return "LFS is currently not in a suitable mode to load a replay from. If you see this error, please report it to the author of LFS Record.";
                default:
                    return "No error";
            }
        }
    }
}
